////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001-2009.
// -------------------------------------------------------------------------
//  File name:   RenderMesh2.h
//  Version:     v1.00
//  Created:     01/07/2009 by Andrey Honich.
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#ifndef __RenderMesh2_h__
#define __RenderMesh2_h__
#pragma once

#include <CryPool/PoolAlloc.h>
#include <VectorMap.h>
#include <VectorSet.h>
#include <GeomQuery.h>

#if defined(PS3) /* || defined(XENON) */
#  define USE_VBIB_PUSH_DOWN 0
#endif 

// Enable the below to get fatal error is some holds a rendermesh buffer lock for longer than 1 second
//#define RM_CATCH_EXCESSIVE_LOCKS

#define STALL true

#pragma warning(disable: 4355)

struct SIntrusiveList
{
	SIntrusiveList *next;
	SIntrusiveList *prev;
	SIntrusiveList() 
		: next(this)
		, prev(this)
	{

	}

	void link_tail(SIntrusiveList* list) 
	{ 
		insert(list->prev, list); 
	}

	void link_tail(SIntrusiveList& list) 
	{ 
		link_tail(&list); 
	}

	void link_front(SIntrusiveList* list) 
	{	
		insert(list, list->next); 
	}

	void link_front(SIntrusiveList& list) 
	{ 
		link_front(&list); 
	}

	void relink_tail(SIntrusiveList* list) 
	{ 
		erase();
		link_tail(list); 
	}
	void relink_tail(SIntrusiveList& list) 
	{ 
		relink_tail(&list); 
	}

	void relink_front(SIntrusiveList* list) 
	{ 
		erase();
		link_front(list); 
	}

	void relink_front(SIntrusiveList& list) 
	{ 
		relink_front(&list); 
	}

	void insert(SIntrusiveList* prev_item, SIntrusiveList* next_item)
	{
		next_item->prev = this;
		next = next_item;
		prev = prev_item;
		prev_item->next = this;
	}

	void erase()
	{
		next->prev = prev;
		prev->next = next;
		next = prev = this; 
	}

# if !defined(__SPU__)
	template<typename Type, SIntrusiveList Type::*Member>
	Type* entry()
	{
		uintptr_t value = (uintptr_t)this-(uintptr_t)&(((Type*)(NULL))->*Member);
		return alias_cast<Type*>(value);
	}

	template<typename Type, SIntrusiveList (Type::*Member)[2]>
	Type* entry(int threadId)
	{
		uintptr_t value = (uintptr_t)this-(uintptr_t)&((((Type*)(NULL))->*Member)[threadId]);
		return alias_cast<Type*>(value);
	}
# endif

	bool linked() const { return !!((uintptr_t)next^(uintptr_t)this); }
};

class CMeshSubSetIndicesTask : public IThreadTask
{
public:
	CMeshSubSetIndicesTask( IRenderMesh *pSrcRM, uint64 nSubSetMask) :
		m_pSrcRM(pSrcRM),
		m_pIndexRM(NULL),
		m_nMeshSubSetMask(nSubSetMask),
		m_bHasFinished(false)
	{

	}

	~CMeshSubSetIndicesTask()
	{
		// spinlock in case rendermesh is deleted while a task is running
		while( !HasFinished() )
			CrySleep(1); // yield
		memset(this, 0xDE, sizeof(*this) );
	}

	virtual void OnUpdate();
	virtual void Stop() {};

	virtual SThreadTaskInfo* GetTaskInfo() { return &m_threadTaskInfo; }
	bool HasFinished() const { return m_bHasFinished; }

	IRenderMesh* GetIndexRM() const { return m_pIndexRM; }
	uint64 GetMeshSubSetMask() const { return m_nMeshSubSetMask; }

	static void CreateThreadPool();
	static void AddTaskToPool( CMeshSubSetIndicesTask *pTask );
	static void RemoveTaskFromPool( CMeshSubSetIndicesTask *pTask );

private:
	IRenderMesh *m_pSrcRM;							// source mesh to create a new index mesh from
	IRenderMesh *m_pIndexRM;						// when finished: newly created index mesh for this mask, else NULL
	uint64 m_nMeshSubSetMask;						// mask to use
	SThreadTaskInfo m_threadTaskInfo;		// ThreadTask info structure
	volatile bool m_bHasFinished;				// bool flag to check if task has finished

	static ThreadPoolHandle s_hThreadPool;
};

struct CMeshSubSetIndicesTaskEntry
{
	explicit CMeshSubSetIndicesTaskEntry( CMeshSubSetIndicesTask *pTask, uint64 nMeshSubSetMask ) :
		m_pTask(pTask), m_nMeshSubSetMask(nMeshSubSetMask)
	{

	}
	
	explicit CMeshSubSetIndicesTaskEntry( uint64 nMeshSubSetMask ) :
			m_pTask(NULL), m_nMeshSubSetMask(nMeshSubSetMask)
	{

	}

	bool operator<( const CMeshSubSetIndicesTaskEntry &rOther ) const
	{
		return m_nMeshSubSetMask < rOther.m_nMeshSubSetMask;
	}

	uint64 m_nMeshSubSetMask;
	CMeshSubSetIndicesTask *m_pTask;
};

class RenderMesh_hash_int32
{
public:
	enum {	// parameters for hash table
		bucket_size = 4,	  // 0 < bucket_size
		min_buckets = 1024 // min_buckets = 2 ^^ N, 0 < N
	};

	ILINE size_t operator()( int32 key ) const
	{
		key = ~key + (key << 15);
		key = key ^ (key >> 12);
		key = key + (key << 2);
		key = key ^ (key >> 4);
		key = key * 2057;
		key = key ^ (key >> 16);
		return key;
	};

	ILINE bool operator()( const int32& key1,const int32& key2 ) const
	{
		return key1 < key2;
	}
};

struct SBufInfoTable
{
	int OffsTC;
	int OffsColor;
};

struct SMeshDevBuf
{
	void *m_pDevBuf;
	int32 m_nOffset;
	int32 m_nID;

	SMeshDevBuf()
	{
		m_pDevBuf = NULL;
		m_nOffset = 0;
		m_nID = -1;
	}
};

struct SMeshStream
{
	SMeshDevBuf m_DevBuf;    // device buffer
	void *m_pUpdateData;     // system buffer for updating (used for async. mesh updates)
	void *m_pLockedData;     // locked device buffer data 
	int m_nFrameAccess;
	int m_nFrameRequest;
	int m_nFrameUpdate;
	int m_nFrameRead;
	int m_nFrameCreate;
	uint32 m_nLockFlags;
	uint32 m_nElements;

	SMeshStream()
	{
		m_pUpdateData = NULL;
		m_pLockedData = NULL;
		m_nFrameRequest = 0;
		m_nFrameUpdate = -1;
		m_nFrameAccess = -1;
		m_nFrameRead = 0;
		m_nFrameCreate = -1;
		m_nLockFlags = 0;
		m_nElements = 0;
	}
};

#define RM_INSTANCES 8
#define RM_MAXVERTS_FOR_INSTANCING 200

// CRenderMesh::m_nFlags
#define FRM_RELEASED 1
#define FRM_INSTANCED 2
#define FRM_READYTOUPLOAD 4
#define FRM_ALLOCFAILURE 8
#define FRM_SKINNED 0x10
#define FRM_SKINNEDNEXTDRAW 0x20 // no proper support yet for objects that can be skinned and not skinned.
#define FRM_SUPPORT_HW_TESSELLATION 0x40

#define MAX_RELEASED_MESH_FRAMES (2)

struct SSetMeshIntData
{
	CMesh *m_pMesh;
	SVF_P3S_C4B_T2S *m_pVBuff;
	SVF_P2S_N4B_C4B_T1F *m_pVBuffV;
	SVF_C4B_C4B *m_pSHBuf;
	SPipTangents *m_pTBuff;
	SQTangents *m_pQTBuff;
	SVF_P3F *m_pMorphTargets;
	uint32 m_nVerts;
	uint32 m_nInds;
	uint16 *m_pInds;
	const Vec3 *m_pPosOffset;
	uint32 m_flags;
	SVF_P3F_P3F_I4B *m_pShapeDeformBuff;
	Vec3 *m_pNormalsBuff;
};


class CRenderMesh : public IRenderMesh
{
	friend class CREMeshImpl;

public:
	//Optim - reduce cache miss. Moved m_nFrameRender here as it's only ever accessed alongside m_eVF
	//uint32                  m_nFrameRender;
private:

	SMeshStream m_VBStream[VSF_NUM];
	SMeshStream m_IBStream;
#if defined(DIRECT3D10) && !defined(PS3) && !defined(CAFE)
public:
	WrappedDX11Buffer m_adjBuffer; // buffer containing adjacency information to fix displacement seams
#endif

	EVertexFormat m_eVF;          // Base stream vertex format (optional streams are hardcoded: VSF_)	
	
	uint32 m_nInds;
	uint32 m_nVerts;
	uint32 m_nFlags									: 8;          // FRM_
	eRenderPrimitiveType m_nPrimetiveType			: 6;	
	uint32 m_eType									: 2;	  

#ifdef KEEP_POSITIONS_INDICES
	Vec3f16 *m_pSysPosData;       // positions (PS3 only)
#endif

#ifdef USE_VBIB_PUSH_DOWN
	int32 m_VBIBFramePushID;				//need to know the frame id of the rsx push to not release it too early
#endif

#ifdef FP16_MESH
	Vec3 *m_pCachePos;         // float positions (cached)
	int   m_nFrameRequestCachePos;
	uint32 m_nFlagsCachePos;
#endif

	CRenderMesh *m_pVertexContainer;
	PodArray<CRenderMesh*>  m_lstVertexContainerUsers;
    
#ifdef RENDER_MESH_TRIANGLE_HASH_MAP_SUPPORT
	typedef stl::hash_map<int,PodArray< std::pair<int,int> >,RenderMesh_hash_int32> TrisMap;
	TrisMap * m_pTrisMap;
#endif
	uint32 *         m_arrVtxMap;                //!< [Anton] mapping table RenderMesh vertex idx->original vertex idx

public:
	enum ESizeUsageArg
	{
		SIZE_ONLY_SYSTEM = 0,
		SIZE_VB = 1,
		SIZE_IB = 2,
	};

private:
	bool UpdateVidIndices(SMeshStream& IBStream, bool stall=true);

	bool CreateVidVertices(
		int nVerts=0, EVertexFormat eVF=eVF_Unknown, int nStream=VSF_GENERAL);

	bool UpdateVidVertices(int nStream, bool stall=true);

	bool CopyVBToSystemForUpdate(int nStream);
	bool CopyIBToSystemForUpdate();

	void ReleaseVB(int nStream);
	void ReleaseIB();

	void InitTriHash(IMaterial * pMaterial);
#ifdef FP16_MESH
	bool CreateCachePos(byte *pSrc, uint32 nStrideSrc, uint32 nFlags);
	bool PrepareCachePos();
#endif
  
	//Internal versions of funcs - no lock
	bool UpdateVertices_Int(const void *pVertBuffer, int nVertCount, int nOffset, int nStream, uint32 copyFlags);
	bool UpdateIndices_Int(const uint16 *pNewInds, int nInds, int nOffsInd, uint32 copyFlags);
	size_t SetMesh_Int( CMesh &mesh, int nSecColorsSetOffset, uint32 flags, const Vec3 *pPosOffset);	

#ifdef TESSELLATION_RENDERER
	bool UpdateUVCoordsAdjacency(SMeshStream& IBStream);
	static void BuildAdjacency(SVF_P3S_C4B_T2S *pVerts, int nVerts, uint16 *pIndexBuffer, int nTrgs, std::vector<Vec2f16> &pTxtAdjBuffer);
#endif

	void Cleanup();

public:
	void SetMesh_IntImpl( SSetMeshIntData data );

	//! constructor
	//! /param szSource this pointer is stored - make sure the memory stays
	CRenderMesh(const char *szType, const char *szSourceName, bool bLock=false);
	CRenderMesh();

	//! destructor
	~CRenderMesh();

	VIRTUAL bool CanRender()
	{
		return (m_nFlags & FRM_ALLOCFAILURE) == 0;
	}

	virtual void AddRef() 
	{ 
		CryInterlockedIncrement(&m_nRefCounter); 
	}

	virtual int Release();

	void ReleaseForce()
	{
		while (true)
		{
			int nRef = Release();
			if (nRef <= 0)
				return;
		}
	}

	// ----------------------------------------------------------------
	// Helper functions
	_inline int GetStreamStride(int nStream) const
	{
		if (nStream == VSF_GENERAL)
			return  m_cSizeVF[m_eVF];
		else
			return m_cSizeStream[nStream];
	}

	_inline uint32 _GetFlags() const 
	{ 
		return m_nFlags; 
	}

	_inline int GetStreamSize(int nStream, int nVerts=0) const 
	{ 
		return GetStreamStride(nStream) * (nVerts ? nVerts : m_nVerts); 
	}

	_inline const SMeshDevBuf& _GetVBStream(int nStream) const 
	{ 
		return m_VBStream[nStream].m_DevBuf; 
	}

	_inline const SMeshDevBuf& _GetIBStream() const 
	{ 
		return m_IBStream.m_DevBuf; 
	}

	_inline bool _HasVBStream(int nStream) const 
	{ 
		return m_VBStream[nStream].m_DevBuf.m_nID>=0; 
	}

	_inline bool _HasIBStream() const 
	{ 
		return m_IBStream.m_DevBuf.m_nID>=0; 
	}

	_inline int _IsVBStreamLocked(int nStream) const 
	{
		return (m_VBStream[nStream].m_nLockFlags & FSL_LOCKED); 
	}

	_inline int _IsIBStreamLocked() const 
	{ 
		return m_IBStream.m_nLockFlags & FSL_LOCKED; 
	}

	_inline EVertexFormat _GetVertexFormat() const 
	{ 
		return m_eVF; 
	}

	_inline void _SetVertexFormat(EVertexFormat eVF) 
	{ 
		m_eVF = eVF; 
	}

	_inline int _GetNumVerts() const 
	{ 
		return m_nVerts; 
	}

	_inline void _SetNumVerts(int nVerts) 
	{ 
		m_nVerts = nVerts; 
	}

	_inline int _GetNumInds() const 
	{ 
		return m_nInds; 
	}

	_inline void _SetNumInds(int nInds) 
	{ 
		m_nInds = nInds; 
	}

	_inline const eRenderPrimitiveType _GetPrimitiveType() const 
	{ 
		return m_nPrimetiveType; 
	}

	_inline void _SetPrimitiveType(const eRenderPrimitiveType nPrimType) 
	{ 
		m_nPrimetiveType = nPrimType; 
	}

	_inline void _SetRenderMeshType(uint32 eType) 
	{ 
		m_eType = eType; 
	}

	_inline CRenderMesh *_GetVertexContainer()
	{
		if (m_pVertexContainer)
			return m_pVertexContainer;
		return this;
	}

	size_t Size(uint32 nFlags) const;
	void Size(uint32 nFlags, ICrySizer *pSizer ) const;

	void *LockVB(int nStream, uint32 nFlags, int nOffset=0, int nVerts=0, int *nStride=NULL, bool prefetchIB=false, bool inplaceCachePos=false);

	template<class T>
	T* GetStridedArray(strided_pointer<T>& arr, EStreamIDs stream)
	{
		return arr.data = (T*)LockVB(stream, FSL_READ, 0, 0, &arr.iStride);
	}

	uint16 *LockIB(uint32 nFlags, int nOffset=0, int nInds=0);
	void UnlockVB(int nStream);
	void UnlockIB();

	bool RT_CheckUpdate(CRenderMesh *pVContainer, EVertexFormat eVF, uint32 nStreamMask, bool bTessellation = false, bool stall=STALL);
	void RT_SetMeshCleanup();
	void RT_AllocationFailure();
	bool CheckUpdate(EVertexFormat eVF, uint32 nStreamMask);
	void AssignChunk(CRenderChunk *pChunk, class CREMeshImpl *pRE);
	void InitRenderChunk( CRenderChunk &rChunk );

	void FreeVB(int nStream);
	void FreeIB();
	void FreeDeviceBuffers(bool bRestoreSys);
	void FreeSystemBuffers();
	void FreePreallocatedData();
	void CreateMergedChunks(IMaterial *pMaterial);
	bool SyncAsyncUpdate(int threadId, bool block = true);

	//===========================================================================================
	// IRenderMesh interface
	VIRTUAL const char* GetTypeName()   
	{ 
		return m_sType; 
	}

	VIRTUAL const char* GetSourceName() const 
	{ 
		return m_sSource;
	}

	VIRTUAL int  GetIndicesCount()  
	{ 
		return m_nInds; 
	}

	VIRTUAL int  GetVerticesCount() 
	{ 
		return m_nVerts;
	}

	VIRTUAL EVertexFormat GetVertexFormat() 
	{ 
		return m_eVF; 
	}

	VIRTUAL uint32 GetMeshType() 
	{ 
		return m_eType;
	}

	VIRTUAL void NextDrawSkinned() 
	{ 
		m_nFlags |= FRM_SKINNEDNEXTDRAW;
	}

	VIRTUAL void GenerateQTangents();
	VIRTUAL void CreateChunksSkinned();
	VIRTUAL void CopyTo(IRenderMesh *pDst, int nAppendVtx=0, bool bDynamic=false);
	VIRTUAL void SetSkinningDataVegetation(struct SMeshBoneMapping *pBoneMapping);
	VIRTUAL void SetSkinningDataCharacter(CMesh& mesh, struct SMeshBoneMapping *pBoneMapping);
	// Creates an indexed mesh from this render mesh (accepts an optional pointer to an IIndexedMesh object that should be used)
	VIRTUAL IIndexedMesh* GetIndexedMesh(IIndexedMesh *pIdxMesh=0);
	VIRTUAL int GetRenderChunksCount(IMaterial *pMat, int& nRenderTrisCount);

	VIRTUAL IRenderMesh *GenerateMorphWeights() { return NULL; }
	VIRTUAL IRenderMesh *GetMorphBuddy() { return NULL; }
	VIRTUAL void SetMorphBuddy(IRenderMesh *pMorph) {}

	// Create render buffers from render mesh. Returns the final size of the render mesh or ~0U on failure
	VIRTUAL size_t SetMesh( CMesh &mesh, int nSecColorsSetOffset=0, uint32 flags=0,  const Vec3 *pPosOffset=0, bool requiresLock=true);	

	// Update system vertices buffer
	VIRTUAL bool UpdateVertices(const void *pVertBuffer, int nVertCount, int nOffset, int nStream, uint32 copyFlags, bool requiresLock=true);
	// Update system indices buffer
	VIRTUAL bool UpdateIndices(const uint16 *pNewInds, int nInds, int nOffsInd, uint32 copyFlags, bool requiresLock=true);

	VIRTUAL void SetCustomTexID( int nCustomTID );
	VIRTUAL void SetChunk(int nIndex, CRenderChunk &chunk);
	VIRTUAL void SetChunk(IMaterial *pNewMat, int nFirstVertId, int nVertCount, int nFirstIndexId, int nIndexCount, float texelAreaDensity, int nMatID = 0);

	VIRTUAL void SetRenderChunks( CRenderChunk *pChunksArray,int nCount,bool bSubObjectChunks );

	VIRTUAL PodArray<CRenderChunk>& GetChunks() 
	{ 
		return m_Chunks;
	}

	VIRTUAL PodArray<CRenderChunk>* GetChunksSkinned() 
	{
		return m_pChunksSkinned; 
	}

	VIRTUAL PodArray<CRenderChunk>* GetChunksSubObjects()
	{ 
		return &m_ChunksSubObjects;
	}

	VIRTUAL IRenderMesh *GetVertexContainer() 
	{ 
		return _GetVertexContainer();
	}

	VIRTUAL void SetVertexContainer(IRenderMesh *pBuf);

	VIRTUAL void Render(const struct SRendParams& rParams, CRenderObject* pObj, IMaterial *pMaterial, bool bSkinned=false);
	VIRTUAL void Render(CRenderObject * pObj, const SRenderObjectModifier * pROII=NULL);
	VIRTUAL void AddHUDRenderElement(CRenderObject* pObj, IMaterial* pMaterial);
	VIRTUAL void AddRenderElements(IMaterial * pIMatInfo, CRenderObject * pObj=0, int nSortId=EFSLIST_GENERAL, int nAW=1);
	VIRTUAL void SetREUserData(float * pfCustomData, float fFogScale=0, float fAlpha=1);
	VIRTUAL void AddRE(IMaterial * pMaterial, CRenderObject * pObj, IShader * pEf, int nList=EFSLIST_GENERAL, int nAW=1);

	VIRTUAL void DrawImmediately();

	VIRTUAL byte *GetPosPtr(int32& nStride, uint32 nFlags, int32 nOffset=0);
	VIRTUAL byte *GetNormPtr(int32& nStride, uint32 nFlags, int32 nOffset=0);
	VIRTUAL byte *GetColorPtr(int32& nStride, uint32 nFlags, int32 nOffset=0);
	VIRTUAL byte *GetUVPtr(int32& nStride, uint32 nFlags, int32 nOffset=0);

	VIRTUAL byte *GetTangentPtr(int32& nStride, uint32 nFlags, int32 nOffset=0);
	VIRTUAL byte *GetBinormalPtr(int32& nStride, uint32 nFlags, int32 nOffset=0);

	VIRTUAL byte *GetHWSkinPtr(int32& nStride, uint32 nFlags, int32 nOffset=0);
	VIRTUAL byte *GetShapePtr(int32& nStride, uint32 nFlags, int32 nOffset=0);
	VIRTUAL byte *GetMorphTargetPtr(int32& nStride, uint32 nFlags, int32 nOffset=0);

	VIRTUAL void UnlockStream(int nStream);
	VIRTUAL void UnlockIndexStream();

	VIRTUAL uint16 *GetIndexPtr(uint32 nFlags, int32 nOffset=0);
	VIRTUAL const PodArray<std::pair<int,int> > * GetTrisForPosition(const Vec3 & vPos, IMaterial * pMaterial);
  
	VIRTUAL float GetExtent(EGeomForm eForm);
	VIRTUAL void GetRandomPos(PosNorm& ran, EGeomForm eForm, SSkinningData const* pSkinning = NULL);
	VIRTUAL uint32 *GetPhysVertexMap() { return m_arrVtxMap; }
	VIRTUAL void SetPhysVertexMap(uint32 * pVtxMap) { m_arrVtxMap = pVtxMap; }
	VIRTUAL bool IsEmpty();

	VIRTUAL size_t GetMemoryUsage( ICrySizer* pSizer, EMemoryUsageArgument nType ) const;
	VIRTUAL void GetMemoryUsage( ICrySizer* pSizer ) const;
	VIRTUAL float GetAverageTrisNumPerChunk(IMaterial * pMat);
	VIRTUAL int GetTextureMemoryUsage( const IMaterial* pMaterial, ICrySizer* pSizer = NULL, bool bStreamedIn = true ) const;
	// Get allocated only in video memory or only in system memory.
	VIRTUAL int GetAllocatedBytes( bool bVideoMem ) const;

	VIRTUAL void SetBBox(const Vec3& vBoxMin, const Vec3& vBoxMax) { m_vBoxMin = vBoxMin; m_vBoxMax = vBoxMax; }
	VIRTUAL void GetBBox(Vec3& vBoxMin, Vec3& vBoxMax) { vBoxMin = m_vBoxMin; vBoxMax = m_vBoxMax; };
	VIRTUAL void UpdateBBoxFromMesh();

	// Debug draw this render mesh.
	VIRTUAL void DebugDraw(const struct SGeometryDebugDrawInfo &info, uint32 nVisibleChunksMask=~0, float fExtrdueScale = 0.01f );
	VIRTUAL void KeepSysMesh(bool keep);  // HACK: temp workaround for GDC-888
	VIRTUAL void UnKeepSysMesh();
	VIRTUAL void LockForThreadAccess(); 
	VIRTUAL void UnLockForThreadAccess();

	VIRTUAL void SetMeshLod( int nLod ) 
	{
		m_nLod = nLod;
	}

	VIRTUAL volatile int* SetAsyncUpdateState();

	VIRTUAL void OffsetPosition(const Vec3& delta) 
	{ 
		m_vBoxMin += delta; 
		m_vBoxMax += delta;
	}

	IRenderMesh* GetRenderMeshForSubsetMask(SRenderObjData *pOD, uint64 nMeshSubSetMask, IMaterial * pMaterial);

	void GarbageCollectSubsetRenderMeshes();

	void ReleaseRenderChunks(PodArray<CRenderChunk> * pChunks);
	void CleanupMeshSubSetIndicesTasks();

	// --------------------------------------------------------------
	// Members

	static int32 m_cSizeVF[eVF_Max];
	static int32 m_cSizeStream[VSF_NUM];
	static SBufInfoTable m_cBufInfoTable[eVF_Max];

	// When modifying or traversing any of the lists below, be sure to always hold the link lock 
	static CryCriticalSection m_sLinkLock;

	// intrusive list entries - a mesh can be in multiple lists at the same time 
	SIntrusiveList          m_Chain; // mesh will either be in the mesh list or garbage mesh list
	SIntrusiveList          m_Dirty[2]; // if linked, mesh has volatile data (data read back from vram)
	SIntrusiveList          m_Modified[2]; // if linked, mesh has modified data (to be uploaded to vram)

	// The static list heads, corresponds to the entries above 
	static SIntrusiveList m_MeshList;
	static SIntrusiveList m_MeshGarbageList[MAX_RELEASED_MESH_FRAMES];
	static SIntrusiveList m_MeshDirtyList[2];
	static SIntrusiveList m_MeshModifiedList[2];

	PodArray<CRenderChunk>  m_Chunks;
	PodArray<CRenderChunk>  m_ChunksMerged;     // Used for shadow-gen
	PodArray<CRenderChunk>  m_ChunksSubObjects; // Chunks of sub-objects.
	PodArray<CRenderChunk>* m_pChunksSkinned;

	int                     m_nRefCounter;
	const char*             m_sType;          //!< pointer to the type name in the constructor call
	const char*             m_sSource;        //!< pointer to the source  name in the constructor call

	int                     m_nClientTextureBindID;
	void                   *m_pCustomData;
	Vec3                    m_vBoxMin;
	Vec3                    m_vBoxMax;

	int                     m_nLod; // used for LOD debug visualization

	CGeomExtents						m_Extents;

	// Frame id when this render mesh was last rendered.
	uint32                  m_nLastRenderFrameID;
	uint32                  m_nLastSubsetGCRenderFrameID;

	int						m_nThreadAccessCounter; // counter to ensure that no system rendermesh streams are freed since they are in use
	volatile int  m_asyncUpdateState[2]; 
	int  m_asyncUpdateStateCounter[2]; 

	// For debugging purposes to catch longstanding data accesses
# if !defined(_RELEASE) && defined(RM_CATCH_EXCESSIVE_LOCKS)
	float m_lockTime; 
# endif 

	typedef VectorMap<uint64,_smart_ptr<IRenderMesh> > MeshSubSetIndices;
	MeshSubSetIndices m_meshSubSetIndices;
	
	typedef VectorSet<CMeshSubSetIndicesTaskEntry> MeshSubSetIndicesTasks;
	MeshSubSetIndicesTasks m_meshSubSetIndicesTasks;
	
#ifdef RENDER_MESH_TRIANGLE_HASH_MAP_SUPPORT
	CryCriticalSection m_getTrisForPositionLock;
#endif

	CryCriticalSection m_sResLock;

	static void Initialize();
	static void ShutDown();
	static void Tick();
	static void UpdateModified();
	static void UpdateModifiedMeshes(bool bLocked, int threadId);
	static bool ClearStaleMemory(bool bLocked, int threadId);
	static void PrintMeshLeaks();
	static void GetPoolStats(SMeshPoolStatistics* stats);

	CRenderMesh * m_pTesselatedRenderMesh; // pre-tessellated version of render mesh
};

#undef STALL
#endif // __RenderMesh2_h__
